let logger = require("pomelo-logger").getLogger(__filename);
let createNewGuestPlayer = require("../entities/player/player").createNewGuestPlayer;
let createNewWechatPlayer = require("../entities/player/player").createNewWechatPlayer;
let createNewWywPlayer = require("../entities/player/player").createNewWywPlayer;
let createFromBson = require("../entities/player/player").createFromBSON;
let DateUtil = require("../../utils/dateUtil");

let PlayerService = function (app) {
    this.app = app;
    this.nextPlayerId = 0;
    this.uidToPlayers = {};
    this.connectionIdToPlayers = {};
};

PlayerService.prototype.initialize  = function () {
    let self = this;
    let playerDao = this.app.get("playerDao");
    playerDao.getMaxUid(function (maxPlayerId) {
        let accountConfig = self.app.get("accountConfig");
        if (maxPlayerId < accountConfig["defaultPlayerId"]) {
            maxPlayerId = accountConfig["defaultPlayerId"];
        }
        self.nextPlayerId = maxPlayerId + 1;
        logger.info("playerService initialize nextPlayerId =" + self.nextPlayerId);
        //TODO: ready 以后才可以接收消息
    });
};

PlayerService.prototype.login = function (msg, session, callback) {
    logger.info("player login. msg=" + JSON.stringify(msg));
    let connectionId = session["frontendId"] + "_" + session["id"];
    if (this.connectionIdToPlayers.hasOwnProperty(connectionId)) {
        logger.error("connectionId = " + connectionId + " send login again");
        callback(null, {
            resultCode: 404,
            message: "重复登录，请重新登录"
        });
        return;
    }

    //TODO: check version, user agent, language
    let token = msg["token"];
    let version = msg["version"];
    let userAgent = msg["userAgent"];
    let language = msg["language"];

    if (!token) {
        callback(null, {
            resultCode: -100
        });
        return;
    }

    let self = this;
    let playerDao = this.app.get("playerDao");
    playerDao.findByToken(token, function (document) {
        if (!document) {
            logger.error("query player by token failed. token=" + token);
            callback(null, {
                resultCode: -100
            });
            return;
        }
        let player = createFromBson(document);
        if (player.isTokenExpired()) {
            logger.warn("token is expired. token=" + token + " playerUid=" + player.getUid());
            callback(null, {
                resultCode: -100
            });
            return;
        }
        if (!player.isEnabled()) {
            callback(null, {
                resultCode: 404,
                message: player.getDisableReason() ? player.getDisableReason() : "被禁止登录，请联系客服"
            });
            return;
        }
        callback(null, {
            resultCode: 0,
            message: "ok"
        });
        if (self.uidToPlayers.hasOwnProperty(player.getUid())) {
            player = self.uidToPlayers[player.getUid()];
            let prevFrontendId = player.getFrontendId();
            let prevSessionId = player.getSessionId();
            logger.warn("login in other connection, pre login session will broken. uid=" + player.getUid() +
                " preFrontendId=" + prevFrontendId + " preSessionId=" + prevSessionId);
            player.logout();
            player.setConnectionId(session);
            self.connectionIdToPlayers[connectionId] = player;
            delete self.connectionIdToPlayers[prevFrontendId + "_" + prevSessionId];
            self.app.get("backendSessionService").kickBySid(prevFrontendId, prevSessionId);
        } else {
            player.setConnectionId(session);
            logger.info("login success. uid=" + player.getUid() + " connectionId=" + player.getConnectionId());
            self.uidToPlayers[player.getUid()] = player;
            self.connectionIdToPlayers[player.getConnectionId()] = player;
        }
        player.login();
        self.save(player);
        let gameService = self.app.get("gameService");
        player.pushInitializePlayer();
        player.pushInitializeGames(gameService.toJSON());
        let activityService = self.app.get("activityService");
        player.pushInitializeActivity(activityService.getEffectActivityJSON());
        let swiperItemService = self.app.get("swiperItemService");
        player.pushInitializeSwiper(swiperItemService.getEffectSwiperJSON());
        let announcementService = self.app.get("announcementService");
        player.pushInitializeAnnouncement(announcementService.getEffectAnnouncementsJSON());
        let businessConfig = self.app.get("businessConfig");
        player.pushSetBusinessConfig(businessConfig);
        let configService = self.app.get("configService");
        player.pushSetConfig(configService.toEffectiveJSON());
        player.pushSetAllMails();
        let game = player.getPlayingGame();
        if (game != null) {
            self.app.rpc[game.getName()].gameRemote.notifyPlayerOnline(null, {
                frontendId: session.frontendId,
                sessionId: session.id,
                uid : player.getUid()
            }, function (gamePlayer) {
                if (gamePlayer == null) {
                    player.leave();
                    self.save(player);
                    logger.error("account server and game server sync error");
                    player.pushEnterLobby();
                } else {
                    logger.info("player reconnect ok....");
                }
            });
        } else {
            player.pushEnterLobby();
        }

        // 用户登录 发送mq
        let amqpService = self.app.get("amqpService");
        let exchange = 'kd_game_log_ex';
        let routeKey = 'game_log';
        let msg = {type : 'user_login', uid : player.getUid(), time : DateUtil.nowMS(), channel : player.getUserChannel()};
        amqpService.sendMsg(exchange, routeKey, msg);
    });
};

PlayerService.prototype.logout = function (frontendId, sessionId) {
    //TODO: 与login有重入问题，要通过队列解决
    let player = this.findByConnectionId(frontendId, sessionId);
    if (!player) {
        logger.error("invalid player logout. frontendId=" + frontendId + " sessionId=" + sessionId);
        return;
    }
    player.logout();
    this.save(player);
    delete this.connectionIdToPlayers[frontendId + "_" + sessionId];
    delete this.uidToPlayers[player.getUid()];
};

PlayerService.prototype.findByConnectionId = function (frontendId, sessionId) {
    let connectionId = frontendId + "_" + sessionId;
    if (this.connectionIdToPlayers.hasOwnProperty(connectionId)) {
        return this.connectionIdToPlayers[connectionId];
    } else {
        return null;
    }
};

PlayerService.prototype.findByUid = function (uid, callback) {
    let player = this.getOnlinePlayerByUid(uid);
    if (!!player) {
        callback(player);
        return;
    }
    //TODO 如果此时玩家突然上线， 会发生race condition
    let playerDao = this.app.get('playerDao');
    playerDao.findByUid(uid, function (document) {
        logger.info("playerDao uid "+ uid + " findByUid " + document);
        if (!document) {
            callback(null);
            return;
        }
        callback(createFromBson(document));
    });
};

PlayerService.prototype.findByUnionid = function (unionid, callback) {
    let playerDao = this.app.get('playerDao');
    playerDao.findByUnionid(unionid, function (document) {
        logger.info("playerDao unionid "+ unionid + " findByUnionid " + document);
        if (!document) {
            callback(null);
            return;
        }
        callback({unionid : document["unionid"], uid : document["uid"], userChannel : document["userChannel"]});
    });
};

PlayerService.prototype.exchangeCash = function (uid, exchangeType, itemIndex, callback) {
    let self = this;
    let playerDao = this.app.get('playerDao');
    let shopConfig = null;
    if (exchangeType === "gold") {
        shopConfig = require('../../../config/goods').diamond2Gold;
    } else if (exchangeType === "fangka"){
        shopConfig = require('../../../config/goods').diamond2Fangka;
    } else {
        callback({resultCode : 404, message : "无此兑换类型"});
    }
    logger.info('兑换货币，itemIndex：%s', itemIndex);
    var orderItem = shopConfig[itemIndex];
    if (!orderItem){
        callback({resultCode : 404, message : "无效兑换项"});
        return;
    }
    logger.info('兑换货币，findByUid：%s', uid);
    let diamond = orderItem.price;
    let cash = orderItem.money;
    this.findByUid(uid, function (player) {
        if (!player) {
            logger.info('兑换货币，无此玩家');
            callback({resultCode : 404, message : "无此玩家"});
            return;
        }
        if (player.getDiamond() < diamond) {
            logger.info('兑换货币，钻石不足');
            callback({resultCode : 404, message : "钻石不足"});
            return;
        }
        logger.info('兑换货币，钻石：%s，类型：%s，货币：%s', diamond, exchangeType, cash);
        player.changeDiamond(-diamond);
        if (exchangeType === "gold") {
            player.changeGold(cash);
        } else if (exchangeType === "fangka"){
            player.changeFangka(cash);
        }
        playerDao.save(player, function (result) {});
        if (player.isOnline()) {
            player.pushUpdateSelfCurrency();
        }
        logger.info('兑换成功，钻石：%s，金币：%s，房卡：%s', player.getDiamond(), player.getGold(), player.getFangka());
        callback({resultCode : 0, message : "成功"});
        // 通知MQ
        let amqpService = self.app.get("amqpService");
        let exchange = 'kd_game_log_ex';
        let routeKey = 'game_log';
        if (!!diamond && !!cash && !!uid){
            let msg = {
                type : 'currency_exchange',
                uid : uid,
                amount: diamond,
                action : 'exchange',
                currencyType : 'diamond',
                time : new Date().getTime(),
                channel : player.getUserChannel()
            };
            amqpService.sendMsg(exchange, routeKey, msg);
            msg = {
                type : 'currency_exchange',
                uid : uid,
                amount: cash,
                action : 'exchange',
                currencyType : exchangeType,
                time : new Date().getTime(),
                channel : player.getUserChannel()
            };
            amqpService.sendMsg(exchange, routeKey, msg);
        } else {
            logger.error("exchangeCash ############ error" + amount + " uid=" + uid + " action=" + param);
        }
    });
};

PlayerService.prototype.changeDiamond = function (uid, amount, param) {
    let self = this;
    let playerDao = this.app.get('playerDao');
    this.findByUid(uid, function (player) {
        if (!player) {
            return;
        }
        player.changeDiamond(amount);
        playerDao.save(player, function (result) {});
        if (player.isOnline()) {
            player.pushUpdateSelfCurrency();
        }
        // 通知MQ
        let amqpService = self.app.get("amqpService");
        let exchange = 'kd_game_log_ex';
        let routeKey = 'game_log';
        if (!!amount && !!uid){
            let msg = {
                type : 'user_cost',
                uid : uid,
                currencyType : 'diamond',
                amount: amount,
                action : param,
                time : new Date().getTime(),
                channel : player.getUserChannel()
            };
            amqpService.sendMsg(exchange, routeKey, msg);
        } else {
            logger.error("changeDiamond ############ error" + amount + " uid=" + uid + " action=" + param);
        }
        if (param == "niuniu_start" || param == "zhajinhua_start" || param == "zhuoji_start") {
            if (!!amount && !!uid){
                let msg = {
                    type : 'user_service',
                    uid : uid,
                    currencyType : 'diamond',
                    amount: amount,
                    action : param,
                    time : new Date().getTime(),
                    channel : player.getUserChannel()
                };
                amqpService.sendMsg(exchange, routeKey, msg);
            }
        }
    });
};

PlayerService.prototype.changeGold = function (uid, amount, param) {
    let playerDao = this.app.get('playerDao');
    this.findByUid(uid, player => {
        if (!player) {
            return;
        }
        player.changeGold(amount);
        playerDao.save(player, function (result) {});
        if (player.isOnline()) {
            player.pushUpdateSelfCurrency();
        }
        // 通知MQ
        let amqpService = this.app.get("amqpService");
        let exchange = 'kd_game_log_ex';
        let routeKey = 'game_log';
        if (!!amount && !!uid){
            let msg = {
                type : 'user_cost',
                uid : uid,
                currencyType : 'gold',
                amount: amount,
                action : param,
                time : new Date().getTime(),
                channel : player.getUserChannel()
            };
            amqpService.sendMsg(exchange, routeKey, msg);
        } else {
            logger.error("changeGold ############ error" + amount + " uid=" + uid + " action=" + param);
        }
        if (param == "niuniu_start" || param == "zhajinhua_start" || param == "zhuoji_start") {
            if (!!amount && !!uid){
                let msg = {
                    type : 'user_service',
                    uid : uid,
                    currencyType : 'gold',
                    amount: amount,
                    action : param,
                    time : new Date().getTime(),
                    channel : player.getUserChannel()
                };
                amqpService.sendMsg(exchange, routeKey, msg);
            }
        }
    });
};

PlayerService.prototype.findByNickname = function (nickname, callback) {
    let playerDao = this.app.get('playerDao');
    playerDao.findByNickname(nickname, function (document) {
        logger.info("playerDao nickname "+ nickname + " findByNickname " + document);
        if (!document) {
            callback(null);
            return;
        }
        callback(createFromBson(document));
    });
};

PlayerService.prototype.findAllOnlinePlayers = function () {
    return this.uidToPlayers;
};

PlayerService.prototype.getOnlinePlayerByUid = function (uid) {
    if (this.uidToPlayers.hasOwnProperty(uid)) {
        return this.uidToPlayers[uid];
    } else {
        return null;
    }
};

PlayerService.prototype.getToken = function (msg, callback) {
    logger.info("get Token=" + JSON.stringify(msg));
    if (!msg.hasOwnProperty("type")) {
        logger.error("invalid params. type is empty");
        callback(null);
        return;
    }
    let type = msg["type"];
    if (type === 1 || type === "1") {
        this.createGuestPlayer(callback);
    } else if (type === 2 || type === "2") {
        this.loadOrCreateWechatPlayer(msg, callback);
    } else if (type === 3 || type === "3") {
        this.loadOrCreateWanyiwanPlayer(msg, callback);
    } else {
        logger.error("invalid type=" + msg["type"]);
    }
};

PlayerService.prototype.createGuestPlayer = function (callback) {
    let uid = this.nextPlayerId;
    this.nextPlayerId = this.nextPlayerId + 1;
    let player = createNewGuestPlayer(uid);
    let playerDao = this.app.get("playerDao");
    playerDao.insert(player.toBSON(), function (id) {
         if (!id) {
             logger.error("insert player error. empty id. player=" + JSON.stringify(player.toJSON()));
             callback(null);
             return;
         }
         callback(player);
    });
};

PlayerService.prototype.loadOrCreateWechatPlayer = function (wechatData, callback) {
    if (!wechatData.hasOwnProperty("openid")) {
        logger.error("create wechat player failed. empty openid. wechatData=" + JSON.stringify(wechatData));
        callback(null);
        return;
    }
    if (!wechatData.hasOwnProperty("nickname")) {
        logger.error("create wechat player failed. empty nickname. wechatData=" + JSON.stringify(wechatData));
        callback(null);
        return;
    }
    if (!wechatData.hasOwnProperty("gender")) {
        logger.error("create wechat player failed. empty gender. wechatData=" + JSON.stringify(wechatData));
        callback(null);
        return;
    }
    if (!wechatData.hasOwnProperty("avatar")) {
        logger.error("create wechat player failed. empty avatar. wechatData=" + JSON.stringify(wechatData));
        callback(null);
        return;
    }
    if (!wechatData.hasOwnProperty("token")) {
        logger.error("create wechat player failed. empty token. wechatData=" + JSON.stringify(wechatData));
        callback(null);
        return;
    }
    if (!wechatData.hasOwnProperty("userChannel")) {
        logger.error("create wechat player failed. empty userChannel. wechatData=" + JSON.stringify(wechatData));
        callback(null);
        return;
    }
    if (!wechatData.hasOwnProperty("unionid")) {
        logger.error("create wechat player failed. empty unionid. wechatData=" + JSON.stringify(wechatData));
        callback(null);
        return;
    }
    //TODO: 有重入问题， 后续解决
    let self = this;
    let playerDao = this.app.get("playerDao");
    playerDao.findByChannelUid(wechatData['openid'], function (document) {
        let player = null;
        if (!!document) {
            document.unionid = wechatData.unionid;
            document.userChannel = wechatData.userChannel;
            player = createFromBson(document);
            player.updateWechatTokenTo(wechatData.token);
            playerDao.save(player.toBSON(), function (result) {
                if (result) {
                    callback(player);
                } else {
                    callback(null);
                }
            });
        } else {
            let uid = self.nextPlayerId;
            self.nextPlayerId = self.nextPlayerId + 1;
            player = createNewWechatPlayer(uid, wechatData);
            playerDao.insert(player.toBSON(), function (id) {
                if (!id) {
                    callback(null);
                    return;
                }
                logger.info("create wechat player. dbId=" + id + " openid=" + wechatData["openid"]);
                player.setDBId(id);
                callback(player);
                //新增用户成功 发送创建用户mq
                let amqpService = self.app.get("amqpService");
                let exchange = 'kd_game_log_ex';
                let routeKey = 'game_log';
                let msg = {
                    type : 'create_user',
                    uid : player.getUid(),
                    unionid : player.accountComponent.getUnionid(),
                    time : DateUtil.nowMS(),
                    channel : player.getUserChannel()
                };
                amqpService.sendMsg(exchange, routeKey, msg);
            });
        }
    });
};

PlayerService.prototype.loadOrCreateWanyiwanPlayer = function (wywData, callback) {
    if (!wywData.hasOwnProperty("openid")) {
        logger.error("create wechat player failed. empty openid. data=" + JSON.stringify(wywData));
        callback(null);
        return;
    }
    let self = this;
    let playerDao = this.app.get("playerDao");
    playerDao.findByChannelUid(wywData['openid'], function (document) {
        let player = null;
        if (!!document) {
            player = createFromBson(document);
            player.touchToken();
            playerDao.save(player.toBSON(), function (result) {
                if (result) {
                    callback(player);
                } else {
                    callback(null);
                }
            });
        } else {
            let uid = self.nextPlayerId;
            self.nextPlayerId = self.nextPlayerId + 1;
            player = createNewWywPlayer(uid, wywData);
            playerDao.insert(player.toBSON(), function (id) {
                if (!id) {
                    callback(null);
                    return;
                }
                logger.info("create wechat player. dbId=" + id + " openid=" + wywData["openid"]);
                player.setDBId(id);
                callback(player);
            });
        }
    });
};

PlayerService.prototype.sendMail = function (mailData) {
    for (let id in this.connectionIdToPlayers) {
        let player = this.connectionIdToPlayers[id];
        if (mailData.isReceiver(player.getUid())) {
            player.sendMail(mailData);
            this.save(player);
            player.pushUpdateMails([mailData.getId()]);
        }
    }
};

PlayerService.prototype.disable = function (uid, disableReason, callback) {
    let self = this;
    this.findByUid(uid, function(player) {
        if(!!player) {
            player.disable(disableReason);
            self.save(player);
            callback({
                resultCode: 0,
                message: "ok"
            });
            if (player.isOnline()) {
                player.kick();
            }
        } else {
            callback({
                resultCode: 404,
                message: "禁用用户失败!用户未找到!"
            });
        }
    });
};

PlayerService.prototype.enable = function (uid, callback) {
    let self = this;
    this.findByUid(uid, function(player){
        if(!!player){
            player.enable();
            self.save(player);
            callback({
                resultCode: 0,
                message: "ok"
            });
        }else{
            callback({
                resultCode: 404,
                message: "启用用户失败!用户未找到!"
            });
        }
    });
};

PlayerService.prototype.findDisablePlayers = function (callback) {
    let playerDao = this.app.get('playerDao');
    playerDao.findByEnabled(false, function (document) {
        if (!document) {
            callback(null);
            return;
        }
        let players = [];
        document.forEach(function (item) {
            players.push(createFromBson(item));
        });
        callback(players);
    });
};

PlayerService.prototype.getAllDisablePlayers = function (callback) {
    this.findDisablePlayers(function(players){
        if(!!players){
            let serializePlayers = [];
            players.forEach(function (player) {
                serializePlayers.push(player.toJSON());
            });
            callback(serializePlayers);
        }else{
            callback(null);
        }
    });
};

PlayerService.prototype.save = function (player) {
    this.app.get('playerDao').save(player.toBSON(), function () {});
};

PlayerService.prototype.getPlayerRegisterInfos = function (uids, startTime, endTime, callback) {
    let playerDao = this.app.get('playerDao');
    playerDao.getPlayerRegisterInfos(uids, startTime, endTime, function (documents) {
        if (!documents) {
            logger.error("getPlayerRegisterInfos failed. documents []");
            callback([]);
            return;
        }
        let players = [];
        documents.forEach(function (document) {
            players.push(createFromBson(document));
        });
        callback(players);
    });
};

exports.create = function (app) {
    return new PlayerService(app);
};

